/**
 * 
 */
package networkRmi;

import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Deque;
import java.util.logging.Level;
import java.util.logging.Logger;

import messages.Message;

/**
 * 
 * Description:
 * The CommunicationServer is the basic module for the remote communication.
 * It provides the functions to register to a local rmiregistry,
 * send and receive Message(s) .
 *
 */
public class CommunicationServer extends UnicastRemoteObject 
	implements CommunicationServerRemoteAPI {
	
	private static final long serialVersionUID = 2028392788640926244L;

	/**
	 * Design pattern: SINGLETON
	 * A node is supposed to use only one CommunicationServer
	 * at a time.
	 */
	private static  CommunicationServer comServer = null;
	
	private final Integer rmiregistryLocalPort = 1099;
	private String serviceNodeName="NetworkNode";

	private Deque<Message> receivedMessages;
	
	private Logger logger;
	
	protected CommunicationServer(Deque<Message> msgQueue) throws RemoteException {
		super();
		this.receivedMessages = msgQueue;
		logger = Logger.getLogger(CommunicationServer.class.getName());
		logger.setLevel(Level.OFF);
	}
	
	/**
	 * 
	 */
	public CommunicationServer() throws RemoteException { }

	public static CommunicationServer getComunicationServer(Deque<Message> msgQueue){
		if(comServer == null){
			try {
				comServer = new CommunicationServer(msgQueue);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
		return comServer;
	}
	
	public static CommunicationServer getComunicationServer(){
		if(comServer == null){
			try {
				comServer = new CommunicationServer();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
		return comServer;
	}

	public void registryToRmiregistry() throws RemoteException{
		registryToRmiregistry(serviceNodeName);
	}
	
	public void registryToRmiregistry(String parName) throws RemoteException  {
		System.out.println("Creating the ComunicationServer..");
		this.serviceNodeName = parName;
		Registry registry;
		CommunicationServerRemoteAPI remoteObject_stub;
		
		if (comServer != null) {
			if (System.getSecurityManager() == null) {
				System.setSecurityManager(new SecurityManager());
			}
			UnicastRemoteObject.unexportObject(comServer, true);
			remoteObject_stub = (CommunicationServerRemoteAPI) UnicastRemoteObject.exportObject(comServer, 0);
			String name = parName;
			try {
				System.out.println("Using an existing rmiregistry");
				registry = LocateRegistry.getRegistry(rmiregistryLocalPort.intValue());				
				registry.rebind(name, remoteObject_stub);
			} catch (java.rmi.ConnectException e) {
				System.out.println("Failed!\nTrying to create a new registry");
				registry = LocateRegistry.createRegistry(rmiregistryLocalPort
						.intValue());
				registry.rebind(name, remoteObject_stub);
				System.out.println("done");
			}
			System.out.println("Services registered to rmiregistry on port: "+Registry.REGISTRY_PORT);
			for(String s : registry.list())
				System.out.println("- "+s);
		}else{
			System.err.println("Uninitialized ComunicationServer");
		}
	}

	@Override
	public boolean receiveMessage(Message m) throws RemoteException {
		boolean ret = false;
		synchronized (receivedMessages) {
			ret = receivedMessages.add(m);
			receivedMessages.notify();
		}
		logger.info(Thread.currentThread().getName()+"received message "+m+" from: "+m.getSender());
		return ret;
	}

	@Override
	public boolean sendMessage(Message m, InetAddress host, String service) throws RemoteException, NotBoundException {
		boolean ret = false;
		Registry registry = LocateRegistry.getRegistry(host.getHostAddress());
//		try {
			CommunicationServerRemoteAPI hostServer = 
				(CommunicationServerRemoteAPI) registry.lookup(service);
			ret = hostServer.receiveMessage(m);
			logger.info("sent message "+m+" to "+ host+" at "+service);
//			ret = true;
//		} catch (NotBoundException e) {
//			e.printStackTrace();
//		}
		return ret;
	}

	@Override
	public boolean sendMessage(Message m, InetAddress host)
			throws RemoteException, NotBoundException {
		return sendMessage(m, host, m.getDest().getNodeName());
	}
	
	@Override
	public boolean ping(InetAddress host, String service) throws RemoteException, NotBoundException{
		boolean ret = true;
		Registry registry;
		registry = LocateRegistry.getRegistry(host.getHostAddress());
		CommunicationServerRemoteAPI hostServer = 
			(CommunicationServerRemoteAPI) registry.lookup(service);
		ret = hostServer.pingAnswer();
		return ret;
	}

	/* (non-Javadoc)
	 * @see gossipServices.network.rmi.CommunicationServerRemoteAPI#pingAnswer()
	 */
	@Override
	public boolean pingAnswer() throws RemoteException {
		return true;
	}

//	/**
//	 * @param address
//	 * @param service
//	 */
//	public boolean contactRemoteService(InetAddress address, String service)
//			throws RemoteException, NotBoundException {
//		boolean ret =false;
//		Registry registry;
//		registry = LocateRegistry.getRegistry(address.getHostAddress(), rmiregistryLocalPort.intValue());
//		registry.
//		return ret;
//	}
	
}
